package com.yc;

import com.yc.spring.IocConfig;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MyAnnotationConfigApplicationContext {

    public Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    public Map<String, Object> singletonObjects = new HashMap<>();  //   <id  ,  bean>

    public static void main(String[] args) throws NoSuchMethodException {

        //这行代码试图通过类加载器获取 "com.yc" 这个包的资源路径，
        // 并将其转换为URL对象。然而通常情况下，这种方式只能用来获取类路径（classpath）下的文件或者资源，
        // 所以如果 "com.yc" 是一个包，它可能无法正确地获取到期望的URL。
        //   xxx/xxx/xxx

//        URL url = IocConfig.class.getClassLoader().getResource("com/yc");
//        System.out.println(url);
        MyAnnotationConfigApplicationContext cxt = new MyAnnotationConfigApplicationContext(IocConfig.class);


    }

    public MyAnnotationConfigApplicationContext(Class configClass) {
        //读取配置
        Annotation configClassAnnotation = configClass.getAnnotation(Configuration.class);
        if (configClassAnnotation == null) {
            throw new RuntimeException("不是配置类 " + configClass);
        }

        Object config = null;
        try {
            config = configClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        //1. Bean方法 =>BeanDefinition
        for (Method m : configClass.getDeclaredMethods()) {
            if (m.getAnnotation(Bean.class) != null) {
                //bean方法
                BeanDefinition definition = new BeanDefinition();
                //bean类型
                definition.setClassName(m.getReturnType().getName());
                //
                definition.setBeanMethodName(m);
                //传入config对象   配置类
                definition.setBeanFactory(config);
                definition.setLazyInit(m.getAnnotation(Lazy.class) != null);
                if (m.getAnnotation(Scope.class) != null) {
                    Scope scope = m.getAnnotation(Scope.class);
                    definition.setScope(scope.value());
                }
                Bean beanAnno = m.getAnnotation(Bean.class);
                String[] ids = beanAnno.value();
                //如果 配置类里面 @Bean 没有Id名   默认为方法名
                if (ids.length == 0) {
                    definition.setId(m.getName());
                } else {
                    //取第一个名字
                    definition.setId(ids[0]);
                }
                beanDefinitionMap.put(definition.getId(), definition);
                //其他属性  省略......
            }
        }

        //2.扫描组件  => BeanDefinition
        ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);

        if (componentScan != null) {
            String[] packagePaths = componentScan.value();


            //获取工程类根目录
            URL url1 = configClass.getClassLoader().getResource("");

            File projectRoot = new File(URLDecoder.decode(url1.getPath()));
            //组件类扫描结果集合
            List<File> list = new ArrayList<>();
            for (String path : packagePaths) {
                //使用类加载器获取类路径结构
                ClassLoader loader = configClass.getClassLoader();
                //在java里面  \ 是转义字符  \\代表一个普通的反斜杠
                //在正则表式里面  \  是转义字符
                //   \\.   先在java中转成一个普通的反斜杠    再将 . 转为普通的点
                URL url = loader.getResource(path.replace(".", "/"));
                String rootPackagePath = url.getPath();
                //因为是URL进行的编码  所以需要用URL进行解码
                try {
                    rootPackagePath = URLDecoder.decode(rootPackagePath, "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException("编码集错误", e);
                }
                //扫描的根包文件目录对象
                File rootPath = new File(rootPackagePath);

                //递归扫描 =>
                scanComponent(rootPath, list, projectRoot);

//                list.forEach(System.out::println);
            }
            for (File classFile : list) {
                //字节码文件
                //字节码文件 => 类文件
                //判断是否组件  => 用文件路径去还原一个类路径  => com.yc....
                //  com/yc  =>  com.yc.bbs.User
                String classPath = classFile.getPath().replace(projectRoot.getPath(), "");
                //     \com\yc\spring\bbs\dao\BaseDao.class  =>   com.yc.spring.bbs.dao.BaseDao
                classPath = classPath.replace(".class", "").replaceAll("\\\\", ".").substring(1);
                //加载指定的类
                Class<?> beanClass = null;
                try {
                    beanClass = Class.forName(classPath);

                    if ((beanClass.getAnnotation(Component.class) != null) ||
                            (beanClass.getAnnotation(Service.class) != null) ||
                            (beanClass.getAnnotation(Repository.class) != null) ||
                            (beanClass.getAnnotation(Controller.class) != null)) {
                        //读取Bean的定义信息

                        BeanDefinition definition = new BeanDefinition();
                        //设置类名
                        definition.setClassName(beanClass.getName());
                        definition.setLazyInit(beanClass.getAnnotation(Lazy.class) != null);
                        if (beanClass.getAnnotation(Scope.class) != null) {
                            Scope scope = beanClass.getAnnotation(Scope.class);
                            definition.setScope(scope.value());
                        }

                        //组件命名  如果没有设置 => 类名+首字母小写  User --> user
                        //有设置  取第一个
                        if (beanClass.getAnnotation(Component.class) != null) {
                            if (beanClass.getAnnotation(Component.class).value().length() > 0) {
                                definition.setId(beanClass.getAnnotation(Component.class).value());
                            } else {
                                String name = beanClass.getSimpleName();
                                name = name.substring(0, 1).toLowerCase() + name.substring(1);
                                definition.setId(name);
                            }
                        } else if (beanClass.getAnnotation(Service.class) != null) {
                            if (beanClass.getAnnotation(Service.class).value().length() > 0) {
                                definition.setId(beanClass.getAnnotation(Service.class).value());
                            } else {
                                String name = beanClass.getSimpleName();
                                name = name.substring(0, 1).toLowerCase() + name.substring(1);
                                definition.setId(name);
                            }
                        } else if (beanClass.getAnnotation(Controller.class) != null) {
                            if (beanClass.getAnnotation(Controller.class).value().length() > 0) {
                                definition.setId(beanClass.getAnnotation(Controller.class).value());
                            } else {
                                String name = beanClass.getSimpleName();
                                name = name.substring(0, 1).toLowerCase() + name.substring(1);
                                definition.setId(name);
                            }
                        } else if (beanClass.getAnnotation(Repository.class) != null) {
                            if (beanClass.getAnnotation(Repository.class).value().length() > 0) {
                                definition.setId(beanClass.getAnnotation(Repository.class).value());
                            } else {
                                String name = beanClass.getSimpleName();
                                name = name.substring(0, 1).toLowerCase() + name.substring(1);
                                definition.setId(name);
                            }
                        }
                        beanDefinitionMap.put(definition.getId(), definition);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        /*//测试打印
        beanDefinitionMap.forEach( (k,v) ->{
            System.out.println(v);
        });*/

        //创建Bean
        beanDefinitionMap.forEach((id, beanDefinition) -> {
            if (beanDefinition.getBeanMethodName() == null) {
                //组件扫描
                if (beanDefinition.isLazyInit() || "prototype".equals(beanDefinition.getScope())) {
                    //延迟加载  和 原型模式  ==  现在不创建bean
                } else {
                    try {
                        Class<?> beanClass = Class.forName(beanDefinition.getClassName());
                        Object bean = beanClass.newInstance();
                        //加入容器
                        singletonObjects.put(id, bean);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException("类路径错误！", e);
                    } catch (InstantiationException e) {
                        throw new RuntimeException("创建bean失败！", e);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("方法访问异常！", e);
                    }
                }
            } else {
                //bean方法
                if (beanDefinition.isLazyInit() || "prototype".equals(beanDefinition.getScope())) {
                    //延迟加载  和 原型模式  ==  现在不创建bean
                } else {

                    try {
                        beanDefinition.getBeanMethodName().setAccessible(true);
                        Object bean = beanDefinition.getBeanMethodName().invoke(beanDefinition.getBeanFactory());
                        //加入容器
                        singletonObjects.put(id, bean);
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException("方法调用异常！", e);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("方法访问异常！", e);
                    }

                }
            }
        });


        //执行后置处理器

    }

    /**
     * 递归扫描的方法
     *
     * @param path
     * @param list
     */
    void scanComponent(File path, List<File> list, File projectRoot) {
        File[] files = path.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                scanComponent(file, list, projectRoot);
            } else if (file.isFile() && file.getName().endsWith("class")) {
                //字节码文件 => 类文件
                //判断是否组件  => 用文件路径去还原一个类路径  => com.yc....
                //  com/yc  =>  com.yc.bbs.User
                String classPath = file.getPath().replace(projectRoot.getPath(), "");
                //     \com\yc\spring\bbs\dao\BaseDao.class  =>   com.yc.spring.bbs.dao.BaseDao
                classPath = classPath.replace(".class", "").replaceAll("\\\\", ".").substring(1);
                //加载指定的类
                Class<?> beanClass = null;
                try {
                    beanClass = Class.forName(classPath);

                    if ((beanClass.getAnnotation(Component.class) != null) ||
                            (beanClass.getAnnotation(Service.class) != null) ||
                            (beanClass.getAnnotation(Repository.class) != null) ||
                            (beanClass.getAnnotation(Controller.class) != null)) {
                        //是组件注解   读取Bean的定义信息
                        list.add(file);

                        /*BeanDefinition definition = new BeanDefinition();
                        //设置类名
                        definition.setClassName(beanClass.getName());
                        definition.setLazyInit(beanClass.getAnnotation(Lazy.class) != null);
                        if(beanClass.getAnnotation(Scope.class) != null){
                            Scope scope = beanClass.getAnnotation(Scope.class);
                            definition.setScope(scope.value());
                        }

                        //组件命名  如果没有设置 => 类名+首字母小写  User --> user
                        //有设置  取第一个
                        if( beanClass.getAnnotation(Component.class) != null ){
                           definition.setId( beanClass.getAnnotation(Component.class).value() );
                        }else if( beanClass.getAnnotation(Service.class) != null ){
                            definition.setId( beanClass.getAnnotation(Service.class).value() );
                        }else if( beanClass.getAnnotation(Controller.class) != null ){
                            definition.setId( beanClass.getAnnotation(Controller.class).value() );
                        }else if( beanClass.getAnnotation(Repository.class) != null ){
                            definition.setId( beanClass.getAnnotation(Repository.class).value() );
                        }
                        beanDefinitionMap.put(definition.getId(),definition);*/
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }


    }

    public Object getBean(String id) {
        Object bean = singletonObjects.get(id);
        if (bean == null) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(id);
            if (beanDefinition == null) {
                throw new RuntimeException("没有该bean：" + id);
            } else {
                if ("prototype".equals(beanDefinition.getScope())) {
                    try {
                        Class<?> beanClass = Class.forName(beanDefinition.getClassName());
                        bean = beanClass.newInstance();
                        //加入容器
                        singletonObjects.put(id, bean);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException("类路径错误！", e);
                    } catch (InstantiationException e) {
                        throw new RuntimeException("创建bean失败！", e);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("类访问异常！", e);
                    }
                } else if (beanDefinition.isLazyInit()) {

                    try {
                        if (beanDefinition.getBeanMethodName() == null) {
                            Class<?> beanClass = Class.forName(beanDefinition.getClassName());
                            bean = getBean(beanClass);
                        } else {
                            // 调用工厂方法
                            bean = beanDefinition.getBeanMethodName().invoke(beanDefinition.getBeanFactory());
                        }
                        //加入容器
                        singletonObjects.put(id, bean);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException("类路径错误！", e);
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException("方法调用异常！", e);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("方法访问异常！", e);
                    }

                }
            }

        }
        return bean;
    }

    public <T> T getBean(Class<T> cls) {
        T t = null;
        try {
            t = cls.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("方法调用异常！", e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("方法访问异常！" + e);
        }
        return t;
    }
}
